package ltd.nullpointer.tcp.core.resolver;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import com.boot2.core.HlAssert;
import com.boot2.core.exception.BusinessException;
import com.boot2.core.utils.ByteUtils;
import com.boot2.core.utils.ReflectUtil;
import com.boot2.core.utils.SpringUtil;
import com.boot2.core.utils.StringUtils;
import io.netty.buffer.ByteBuf;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;
import lombok.extern.apachecommons.CommonsLog;
import ltd.nullpointer.tcp.core.TCPMessageProducer;
import ltd.nullpointer.tcp.core.annotation.TcpUpMessage;
import ltd.nullpointer.tcp.core.annotation.TcpUpOffset;
import ltd.nullpointer.tcp.core.constant.TCPEnum;
import ltd.nullpointer.tcp.core.exception.MessageResolverException;
import ltd.nullpointer.tcp.core.message.AbstractTCPMessage;
import ltd.nullpointer.tcp.core.message.NPiotTCPMessage;
import ltd.nullpointer.tcp.core.resolver.parser.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.data.domain.Example;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.persistence.Table;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Callable;

/**
 * 平台业务消息序列化器
 *
 * @author zhangweilin
 * @Description:
 * @date 2018年1月30日 下午3:22:01
 */
@CommonsLog
public abstract class AbstractTCPMessageResolver extends AbstractTCPMessageClassFieldEntry implements MessageResolver {

    /**
     * 解析插件注册
     */
    protected Map<Class<?>, ResolverParser<?>> resolverParserMap;

    /**
     * 注册的报文头
     */
    protected byte[] BEGIN = null;

    /**
     * 是否需要加解密
     */
    private Boolean isNeedEncrypt;

//    @Autowired(required = false)
//    TCPMessageProducer tcpMessageProducer;

    public void regHeader(byte[] headerBytes) {
        this.BEGIN = headerBytes;
        MessageResolverFactory.getInstance().bindMessageAnalyzer(headerBytes, this);
    }

    /**
     * 接受外部传参
     *
     * @param isNeedEncrypt
     */
    public void regIsNeedEncrypt(Boolean isNeedEncrypt) {
        this.isNeedEncrypt = isNeedEncrypt;
    }

    /**
     * 初始化基础信息
     */
    @PostConstruct
    public abstract void regHeader();

    /**
     * 奇怪，通过jaavaassit生成的子类，调用set居然子类获取不到，换成set以外的却可以，故改成reg
     *
     * @param resolverParserMap
     */
    public void regResolverParserMap(Map<Class<?>, ResolverParser<?>> resolverParserMap) {
        this.resolverParserMap = resolverParserMap;
    }

    /**
     * 默认不走加密通道
     *
     * @return
     */
    @Override
    public Boolean isNeedEncrypt() {
        return isNeedEncrypt == null ? false : isNeedEncrypt;
    }

    @Override
    public AbstractTCPMessage resolver(ByteBuf byteBuf) throws MessageResolverException {
        NPiotTCPMessage npiotMessage = new NPiotTCPMessage();
        //todo 考虑用reset的方式，而不是copy
//        ByteBuf copy2 = byteBuf.retainedDuplicate();
        byteBuf.skipBytes(BEGIN.length);

        byte[] typeByte = new byte[1];
        byteBuf.readBytes(typeByte);
        byteBuf.resetReaderIndex();
        String msgCode = ByteUtils.hexBytes2HexString(BEGIN) + ByteUtils.hexBytes2HexString(typeByte);
        typeByte = null;
        npiotMessage.setMsgCode(msgCode);
        //动态解析===
        Object obj = resolver(byteBuf, msgCode, npiotMessage);
        if (null == obj) {
            return null;
        }
        //todo 以后更复杂的协议可能用到，别删除
//        TCPMessageHandler<NPiotTCPMessage> handler = TCPMessageRouter.map.get(msgCode);
//        if (null != handler) {
//            handler.hand(byteBuf, npiotMessage);
//        }
        return npiotMessage;

    }

    protected Object resolver(ByteBuf byteBuf, String msgCode, NPiotTCPMessage npiotMessage) {
        //取bcc
//        ByteBuf byteBufCopy = byteBuf.retainedDuplicate();
        int contentLength = byteBuf.readableBytes();
        log.info("收到报文字节总长度，contentLength = " + contentLength);
        byte[] toBccByteArr = new byte[contentLength - 1];
        npiotMessage.setPayloadByte(toBccByteArr);
        byteBuf.readBytes(toBccByteArr);
        String bcc = ByteUtils.getBCC(toBccByteArr);
        toBccByteArr = null;

        byte[] bccByteArr = new byte[1];

        log.debug("接收 算出的 bcc = " + bcc);
        byteBuf.readBytes(bccByteArr);
        byteBuf.resetReaderIndex();
        String bccHexStr = ByteUtils.hexBytes2HexString(bccByteArr);
        bccByteArr = null;
        log.debug("接收 解析出来的末尾 bccHexStr = " + bccHexStr);
        if (!bcc.equalsIgnoreCase(bccHexStr)) {
            throw new MessageResolverException(TCPEnum.ErrorCode.err10003);
        }

        //解析正文
        ClassFieldEntry classListMap = classFieldEntryMap.get(msgCode);
        HlAssert.notNull(classListMap, "msgCode " + msgCode + " 无效");
        Class<?> clazz = classListMap.getClazz();

        Object proxyObject = null;
        try {
//            proxyObject = ReflectUtil.newProxyInstanceAndAddField(clazz, AccessFlag.PRIVATE, "java.util.Map", "_map");
            proxyObject = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        log.debug("clazz = " + clazz);
        TcpUpMessage tcpMessage = clazz.getAnnotation(TcpUpMessage.class);
//        Object newInstance = null;
//        try {
//            newInstance = clazz.newInstance();
//        } catch (InstantiationException | IllegalAccessException e) {
//            e.printStackTrace();
//        }
        Set<String> propertiesSet = new HashSet<>();
        String[] uniqueProperties = tcpMessage.uniqueProperties();
        Object uniqueCheckSearchInstance = null;
        if (null != uniqueProperties) {
            CollectionUtils.addAll(propertiesSet, uniqueProperties);
            try {
                if (propertiesSet.size() > 0) {
                    uniqueCheckSearchInstance = clazz.newInstance();
                }
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        int tcpStart = tcpMessage.start();
        byteBuf.skipBytes(tcpStart - 1);

        Map<String, Callable<Object>> delayInvokerMap = new HashMap<>();
        //此是已经排好序的
        List<Field> fieldList = classListMap.getFieldList();
        for (int i = 0; i < fieldList.size(); i++) {
            Field field = fieldList.get(i);
            Class<?> type = field.getType();
            //获取setter
            String fieldName = field.getName();
            if (!Collection.class.isAssignableFrom(type)) {
                doResolve(byteBuf, proxyObject, field, delayInvokerMap);
                //todo 如果是集合，则此时肯定不止只剩下一个字节，此时如果剩下只读只有一个字节，说明循环为空（假设最后一位为校验）
            } else if (byteBuf.readableBytes() > 1) {
                List<Object> dataList = new ArrayList<>();
                Class<?> loopClass = null;
                do {
                    Object object2 = null;
                    try {
                        TcpUpOffset tcpOffset = field.getAnnotation(TcpUpOffset.class);
                        loopClass = tcpOffset.loopClass();
                        Assert.notNull(loopClass, "循环数据需要指定具体的class");
                        object2 = ReflectUtil.newProxyInstanceAndAddField(loopClass, AccessFlag.PRIVATE, "java.util.Map", "_map");
                        ReflectUtil.setFieldValue(object2, "_map", ImmutableMap.of(StringUtils.changFirstWord(clazz.getSimpleName(), StringUtils.toLowerCase), proxyObject));
                    } catch (InstantiationException | IllegalAccessException | CannotCompileException | NotFoundException e) {
                        e.printStackTrace();
                    }

                    List<Field> fieldList2 = ReflectUtil.getAllFieldListWithParentAndAnnotation(loopClass, TcpUpOffset.class, "MSG_CODE", "msgCode");
                    for (int j = 0; j < fieldList2.size(); j++) {
                        Field field2 = fieldList2.get(j);
                        doResolve(byteBuf, object2, field2, delayInvokerMap);
                    }
                    dataList.add(object2);
                }
                //todo 为适应更多格式协议，此处以后再调
                while (byteBuf.readableBytes() > 1);
//                set.invoke(proxyObject, new Object[]{dataList});
//                ReflectUtil.setFieldValueBySetter(proxyObject, field, dataList);
                ReflectUtil.setFieldValue(proxyObject, field, dataList);
            }
        }
        try {
            do {
                Set<Map.Entry<String, Callable<Object>>> entrySet = delayInvokerMap.entrySet();
                Iterator<Map.Entry<String, Callable<Object>>> entryIterator = entrySet.iterator();
                while (entryIterator.hasNext()) {
                    Map.Entry<String, Callable<Object>> entry = entryIterator.next();
                    Object callValue = entry.getValue().call();
                    //如果仍然是要延迟状态，则继续计算，如果已经计算成功，跳出循环
                    if (!CalcExpressionResolverParser.ReturnType.Delay.equals(callValue)) {
                        entryIterator.remove();
                        continue;
                    }
                }
            } while (delayInvokerMap.size() > 0);
        } catch (Exception e) {
            e.printStackTrace();
            throw new MessageResolverException(TCPEnum.ErrorCode.err10010.getErrCode(), TCPEnum.ErrorCode.err10010.getName() + ",表达式计算异常 ", e);
        }
//        BeanUtils.copyProperties(proxyObject, newInstance);
        TCPMessageProducer tcpMessageProducer = SpringUtil.getBean(TCPMessageProducer.class);
        if (null != tcpMessageProducer) {
            tcpMessageProducer.handReply(proxyObject);
        }
        //todo 去重
        Table table = clazz.getAnnotation(Table.class);
        if (table != null && tcpMessage.isPersistence()) {
            //如果是需要持久化的，直接持久化
            String simpleName = clazz.getSimpleName();
            simpleName = StringUtils.changFirstWord(simpleName, StringUtils.toLowerCase);
            Object dao = SpringUtil.getBean(simpleName + "Dao");
//
            try {
                //塞查询参数实体,只有propertiesSet有实际的值，才需要校验唯一性
                Set<Object> objectSet = null;
                if (propertiesSet != null && !propertiesSet.isEmpty()) {
                    objectSet = new HashSet<>();
                    for (String uniqueProperty : propertiesSet) {
                        Object value = ReflectUtil.getFieldValue(proxyObject, uniqueProperty);
                        ReflectUtil.setFieldValue(uniqueCheckSearchInstance, uniqueProperty, value);
                        objectSet.add(value);
                    }
                    //如果已经存在了，不必要再往下了，既不要发到tb,也不用持久化
                    Boolean exists = (Boolean) MethodUtils.invokeMethod(dao, "exists", Example.of(uniqueCheckSearchInstance));
                    log.warn("已经存在该值，key: " + propertiesSet + ", value: " + objectSet);
                    if (exists) {
                        return null;
                    }
                }
//                TcpUpMessage tcpUpMessage = clazz.getAnnotation(TcpUpMessage.class);
                MethodUtils.invokeMethod(proxyObject, "setCreateTime");
                MethodUtils.invokeMethod(dao, "saveAndFlush", proxyObject);
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
                throw new BusinessException("保存上报数据失败", e);
            }
        }

        //塞实体值
        npiotMessage.setPayload(proxyObject);
//        log.debug("解析出最终对象,newInstance = " + proxyObject);
        return proxyObject;
    }

    private void doResolve(ByteBuf byteBuf, Object proxyObject, Field field, Map<String, Callable<Object>> delayInvokerMap) {
        String fieldName2 = field.getName();
        TcpUpOffset tcpOffset = field.getAnnotation(TcpUpOffset.class);
        String dateFormat = tcpOffset.dateFormat();
        Class<? extends ResolverParser<?>>[] resolverParserClassArr = tcpOffset.resolverParser();
        String[] resolverParserParamPairArr = tcpOffset.resolverParserParamPair();
        int start = tcpOffset.start();
        int end = tcpOffset.end();
        int length = end - start + 1;

        //先解析到字节数组中
        byte[] byteArr = new byte[length];
        if (!tcpOffset.repeat()) {
            byteBuf.readBytes(byteArr);
        } else {
            ByteBuf buf = byteBuf.copy(byteBuf.readerIndex(), length);
            buf.readBytes(byteArr);
        }
        if (tcpOffset.skip()) {
            return;
        }

        TCPEnum.ResolveType resolveType = tcpOffset.resolveType();
        switch (resolveType) {
            case hexByte2Int:
                ResolverParser<?> resolverParser = resolverParserMap.get(HexByte2IntResolverParser.class);
                parseInt(proxyObject, delayInvokerMap, field, byteArr, resolverParser);
                break;
            case hexByte2UnsignedInt:
                ResolverParser<?> resolverParser2 = resolverParserMap.get(HexByte2UnsignedIntResolverParser.class);
                parseInt(proxyObject, delayInvokerMap, field, byteArr, resolverParser2);
                break;
            case hexBytes2HexString:  //此为默认值
                Object obj = null;
                //如果没有指定解析类型，也没有指定解析转换器，则走默认
                if (resolverParserClassArr[0].equals(HexBytes2HexStringResolverParser.class)) {
                    ResolverParser<?> resolverParser3 = resolverParserMap.get(HexBytes2HexStringResolverParser.class);
                    obj = resolverParser3.resolve(byteArr, proxyObject, null);
//                Object obj = ByteUtils.hexBytes2HexString(byteArr);
                    if (StringUtils.isNotEmpty(dateFormat)) {
                        ResolverParser<?> resolverParser4 = resolverParserMap.get(DateResolverParser.class);
                        obj = resolverParser4.resolve(null, proxyObject, ImmutableMap.of("value", obj, "dateFormat", dateFormat));
                    }
//                    setter.invoke(proxyObject, new Object[]{obj});
//                    ReflectUtil.setFieldValueBySetter(proxyObject, field, obj);
                    ReflectUtil.setFieldValue(proxyObject, field, obj);
                    //如果没有指定解析类型但是指定了别的解析转换器，则走自定义的
                } else {
                    for (int i = 0; i < resolverParserClassArr.length; i++) {
                        Class<? extends ResolverParser<?>> resolverParserClass = resolverParserClassArr[i];
                        ResolverParser<?> customResolverParser = resolverParserMap.get(resolverParserClass);
                        Map<String, Object> paramMap = null;
                        String resolverParserParamPair = null;
                        try {
                            //当配置了多个解析转换器时，允许 不配置参数，但是一旦配置了参数，个数就要一致
                            if (resolverParserParamPairArr.length == 1 && StringUtils.isEmpty(resolverParserParamPairArr[0])) {
                                resolverParserParamPair = null;
                            } else {
                                resolverParserParamPair = resolverParserParamPairArr[i];
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            throw new MessageResolverException(TCPEnum.ErrorCode.err10010.getErrCode(), TCPEnum.ErrorCode.err10010.getName() + ",字段: " + fieldName2 + " ,当指定了多个自定义转换器时，参数个数要与自定义的转换器个数一致");
                        }
                        if (StringUtils.isNotEmpty(resolverParserParamPair)) {
                            paramMap = JSON.parseObject(resolverParserParamPair, Map.class);
                        }
                        obj = customResolverParser.resolve(byteArr, proxyObject, paramMap);
                        if (null != obj) {
//                            setter.invoke(proxyObject, new Object[]{obj});
//                            ReflectUtil.setFieldValueBySetter(proxyObject, field, obj);
                            ReflectUtil.setFieldValue(proxyObject, field, obj);
                        }
                    }
                }
//                npiotMessage.put(fieldName2, obj);
                break;
            case ascii:
                ResolverParser<?> resolverParser5 = resolverParserMap.get(AsciiResolverParser.class);
                Object str2 = resolverParser5.resolve(byteArr, proxyObject, ImmutableMap.of("charsetName", tcpOffset.charsetName()));
//                String str2 = ByteUtils.getString(byteArr, tcpOffset.charsetName());
//                setter.invoke(proxyObject, new Object[]{str2});
//                ReflectUtil.setFieldValueBySetter(proxyObject, field, str2);
                ReflectUtil.setFieldValue(proxyObject, field, str2);
//                npiotMessage.put(fieldName2, str2);
                break;
            case manually:
                //啥也不做，留给手工解析
                break;
            default:
                break;
        }
        byteArr = null;
    }


    private void parseInt(Object proxyObject, Map<String, Callable<Object>> delayInvokerMap, Field field, byte[] byteArr, ResolverParser<?> resolverParser) {
        String fieldName2 = field.getName();
        Class<?> fieldType = field.getType();
        TcpUpOffset tcpOffset = field.getAnnotation(TcpUpOffset.class);
        String calcExpression = tcpOffset.calcExpression();

        Object intValue = resolverParser.resolve(byteArr, proxyObject, null);
        Object calcIntValue = null;
        //初始化第一次计算值
        Object resultValue = intValue;
        if (StringUtils.isNotEmpty(calcExpression)) {
            ResolverParser<?> calcExpressionResolverParser = resolverParserMap.get(CalcExpressionResolverParser.class);
            calcIntValue = calcExpressionResolverParser.resolve(null, proxyObject, ImmutableMap.of("value", intValue, "expression", calcExpression));
            //如果是需要延迟计算，则先缓存着
            if (calcIntValue.equals(CalcExpressionResolverParser.ReturnType.Delay)) {
                delayInvokerMap.put(proxyObject.getClass() + "_" + fieldName2, () -> {
                    Object delayInvokerIntValue = calcExpressionResolverParser.resolve(null, proxyObject, ImmutableMap.of("value", intValue, "expression", calcExpression));
                    //计算出确切结果才赋值
                    if (!delayInvokerIntValue.equals(CalcExpressionResolverParser.ReturnType.Delay)) {
                        if (fieldType.equals(BigDecimal.class) && !(delayInvokerIntValue instanceof BigDecimal)) {
//                            System.out.println("delayInvokerIntValue = " + delayInvokerIntValue);
                            delayInvokerIntValue = new BigDecimal(String.valueOf(delayInvokerIntValue));
                        }
//                    setter.invoke(proxyObject, new Object[]{delayInvokerIntValue});
//                        ReflectUtil.setFieldValueBySetter(proxyObject, field, delayInvokerIntValue);
                        ReflectUtil.setFieldValue(proxyObject, field, delayInvokerIntValue);
                        return delayInvokerIntValue;
                    } else {
                        return CalcExpressionResolverParser.ReturnType.Continue;
                    }
                });
            } else {
                //如果用了表达式且表达式返回了计算出来的值
                resultValue = calcIntValue;
            }
        }
        if (fieldType.equals(BigDecimal.class) && !(resultValue instanceof BigDecimal)) {
            resultValue = new BigDecimal(String.valueOf(resultValue));
        }
//        setter.invoke(proxyObject, new Object[]{resultValue});
//        ReflectUtil.setFieldValueBySetter(proxyObject, field, resultValue);
        ReflectUtil.setFieldValue(proxyObject, field, resultValue);
//        npiotMessage.put(fieldName2, resultValue);
    }
}
